//-------------------------------------------------------------------
//	Copyright (c) 2012-2014 Zhirnov Andrey
//	This file is part of the "UXGEN-ES" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#pragma once

#include "Graphics/GLshell/FrameBuffer.h"
#include "Graphics/Resources/Texture.h"
#include "Graphics/Glshell/GLStateKeeper.h"


namespace Engine
{
namespace Graphics
{

	class RenderSurface;
	class DefaultRenderSurface;
	class FboRenderSurface;

	SHARED_POINTER( RenderSurface );
	SHARED_POINTER( FboRenderSurface );
	SHARED_POINTER( DefaultRenderSurface );


	//
	// Render Surface
	//

	class RenderSurface : public BaseObject
	{
	// variables
	protected:
		uvec2			_dim;
		BatchRender		_batch;
		// TODO: render queue for 3D


	// methods
	public:
		RenderSurface (const EngineSubSystemsRef ess) :
			BaseObject(ess), _batch(ess)
		{}
			

		uvec2 const &	Size ()		const	{ return _dim; }

		BatchRender *	GetBatchRender ()	{ return &_batch; }


	// interface
	public:
		//virtual void Flush () = 0;
	};



	//
	// Default Render Surface
	//

	class DefaultRenderSurface : public RenderSurface
	{
	// variables
	protected:


	// methods
	public:
		DefaultRenderSurface (const EngineSubSystemsRef ess);
			

		// RenderSurface //
		//virtual void Flush () override;
	};


	
	//
	// FBO Render Surface
	//

	class FboRenderSurface : public RenderSurface
	{
	// variables
	protected:
		TexturePtr		_color,
						_depth,
						_stencil;
		FrameBuffer		_fb;


	// methods
	public:
		FboRenderSurface (const EngineSubSystemsRef ess);

		bool Create (const uvec2 &dim, EPixelFormat::type colorFormat,
					 EPixelFormat::type depthFormat = EPixelFormat::_UNKNOWN,
					 EPixelFormat::type stencilFormat = EPixelFormat::_UNKNOWN);

		bool Create (const TexturePtr &colorTarget, const TexturePtr &depthTarget = null,
					 const TexturePtr &stencilTarget = null);
		
		void Reset ();

		TexturePtr const &	ColorTarget ()		const	{ return _color; }
		TexturePtr const &	DepthTarget ()		const	{ return _depth; }
		TexturePtr const &	StencilTarget ()	const	{ return _stencil; }

		void Bind ();
		void Unbind ();

		static FboRenderSurfacePtr	New (const EngineSubSystemsRef ess);

		
		// RenderSurface //
		//virtual void Flush () override;
	};

	

/*
=================================================
	constructor
=================================================
*/
	inline FboRenderSurface::FboRenderSurface (const EngineSubSystemsRef ess) :
		RenderSurface(ess), _fb(ess)
	{
	}
		
/*
=================================================
	Reset
=================================================
*/
	inline void FboRenderSurface::Reset ()
	{
		_dim	 = uvec2();
		_color	 = null;
		_depth	 = null;
		_stencil = null;
		_fb.Destroy();
	}
	
/*
=================================================
	Create
=================================================
*/
	inline bool FboRenderSurface::Create (const uvec2 &dim, EPixelFormat::type colorFormat,
										  EPixelFormat::type depthFormat, EPixelFormat::type stencilFormat)
	{
		GLStateKeeper::FrameBuffer	fb_keeper( ESS() );

		Reset();

		_dim = dim;

		_fb.Create();
		_fb.Bind();

		if ( colorFormat != EPixelFormat::_UNKNOWN )
		{
			_color = Texture::New( ESS() );
			CHECK_ERR( _color->Alloc( uvec3( dim, 0 ), colorFormat ) );
			_fb.Attach( *_color, gl_fbo_attach::COLOR );
		}

		if ( depthFormat != EPixelFormat::_UNKNOWN )
		{
			_depth = Texture::New( ESS() );
			CHECK_ERR( _depth->Alloc( uvec3( dim, 0 ), depthFormat ) );
			_fb.Attach( *_depth, gl_fbo_attach::DEPTH );
		}

		if ( stencilFormat != EPixelFormat::_UNKNOWN )
		{
			_stencil = Texture::New( ESS() );
			CHECK_ERR( _stencil->Alloc( uvec3( dim, 0 ), stencilFormat ) );
			_fb.Attach( *_stencil, gl_fbo_attach::STENCIL );
		}

		CHECK_ERR( _fb.CheckStatus() );

		_fb.SetViewport( ivec4( 0, 0, _dim.x, _dim.y ) );
		return true;
	}
	
/*
=================================================
	Create
=================================================
*/
	inline bool FboRenderSurface::Create (const TexturePtr &colorTarget, const TexturePtr &depthTarget,
										  const TexturePtr &stencilTarget)
	{
		GLStateKeeper::FrameBuffer	fb_keeper( ESS() );

		Reset();

		_fb.Create();
		_fb.Bind();

		if ( colorTarget.IsNotNull() and colorTarget->IsValid() )
		{
			_dim	= colorTarget->Dimension().xy();
			_color	= colorTarget;
			_fb.Attach( *_color, gl_fbo_attach::COLOR );
		}

		if ( depthTarget.IsNotNull() and depthTarget->IsValid() )
		{
			if ( IsNotZero( _dim ) ) {
				CHECK_ERR( All( _dim == depthTarget->Dimension().xy() ) );
			}
			else
				_dim = depthTarget->Dimension().xy();

			_depth = depthTarget;
			_fb.Attach( *_depth, gl_fbo_attach::DEPTH );
		}

		if ( stencilTarget.IsNotNull() and stencilTarget->IsValid() )
		{
			if ( IsNotZero( _dim ) ) {
				CHECK_ERR( All( _dim == stencilTarget->Dimension().xy() ) );
			}
			else
				_dim = stencilTarget->Dimension().xy();

			_stencil = stencilTarget;
			_fb.Attach( *_stencil, gl_fbo_attach::STENCIL );
		}

		CHECK_ERR( _fb.CheckStatus() );

		_fb.SetViewport( ivec4( 0, 0, _dim.x, _dim.y ) );
		return true;
	}
	
/*
=================================================
	Bind
=================================================
*/
	inline void FboRenderSurface::Bind ()
	{
		ESS()->GetGLStateManager()->PushViewport();
		_fb.ApplyViewport();
		_fb.Bind();
	}
	
/*
=================================================
	Unbind
=================================================
*/
	inline void FboRenderSurface::Unbind ()
	{
		_fb.Unbind();
		ESS()->GetGLStateManager()->PopViewport();
	}
	
/*
=================================================
	New
=================================================
*/
	inline FboRenderSurfacePtr FboRenderSurface::New (const EngineSubSystemsRef ess)
	{
		return BaseObject::_New( new FboRenderSurface( ess ) );
	}


}	// Graphics
}	// Engine